#ifndef ROBOT_H
#define ROBOT_H

#include "global.h"

#include <QObject>
#include <QString>
#include <QList>
#include <QStack>
#include <QMatrix4x4>
#include <QSharedPointer>

#include <RobotPose.h>

#include "robotsegment.h"
#include "robotjointgroup.h"
#include "robotvisitor.h"

/**
 * @brief The data structure of a robot.
 *
 * A robot consists of two types of components: segments, which represent the "bones"
 * of the robot, and joint groups, about which the segments rotate. The components
 * of the robot are stored in a heirarchical tree, and this class gives restricted
 * access to those components.
 *
 * The robot gives access to manipulate the joints, and fires events to let other
 * objects know that changes have been made.  There are two events fired,
 * jointAngleChanged() and robotChanged() that have different behaviors.  The first,
 * jointAngleChanged(), always fires at the changing of a joint.  robotChanged(),
 * however, has a special behavior.  Changes to the robot can be made in a "batch",
 * which allows you to make multiple changes to the robot, but only one robotChanged()
 * signal is fired.  This is useful for smoothing out animation, but I'm sure it can
 * be used for other functionality.
 *
*/
class Robot : public QObject
{
    Q_OBJECT

private:
    /**
     * @brief Private class that uses the visitor pattern
     * to traverse a robot and gather information about
     * the joints.
     *
     * The joint information is stored as a map from a joint ID
     * to a pointer of the RobotJointGroup containing that joint.
    */
    class JointVisitor : public RobotVisitor
    {
    private:
        QMap<int, RobotJointGroup*> _jointMap;
    public:
        void pre(){}
        void visit(RobotComponent *component);
        void post(){}
        QMap<int, RobotJointGroup*>* map();
    };


    /**
     * @brief Private class that uses the visitor pattern to
     * traverse the robot and find a certain component.
     *
    */
    class ComponentFinder : public RobotVisitor
    {
    private:
        bool _found;
        RobotComponent* _component;
        NameGroup _componentNames;
    public:
        explicit ComponentFinder(const NameGroup componentNames);
        void pre(){}
        void visit(RobotComponent *component);
        void post(){}
        RobotComponent* component();
    };

protected:
    /**
     * @brief A pointer to the "root" component of the component tree.
    */
    RobotComponent* _rootComponent;

private:
    QString _description;

    //Batches can be nested.  To enable this behavior, we keep a
    //semaphore-like count of how many batches we have
    int _batchSem;
    bool _changed;

    //Helper function that finds the RobotJointGroup that
    //contains a given joint
    RobotJointGroup* getJointGroup(int jointID) const;

    // Mirroring affects only the getJoint functions. Setting joints should be left unaffected.
    // Still working out whether or not this would be better to fit in with the view rather than the model...probably would be.
    bool _mirrored;
    QSet<QPair<int, int> > _mirrorMap;

public:
    /**
     * @brief Constructor.  Creates a new robot.
     *
     * @param description A human-readable description of the robot.
    */
    Robot(QString description = "", QObject* parent = 0);
    virtual ~Robot();

    //Functions for info about the robot
    QString description() const;
    //If the robot is null, it means it is a NullBot, and is not owned by anything.
    //Namely, this is used by the RobotAction class to check to see if it should
    //delete Robots when they are replaced.
    virtual bool isNull() const;

    //Functions for information about the robot's components
    const RobotComponent* getComponent(const NameGroup names) const;

    //Functions for information about the robot's joints
    const QList<int> jointIDs() const;
    float jointAngle(int jointID) const;
    QString jointDescription(int jointID) const;
    float maximum(int jointID) const;
    float minimum(int jointID) const;

    //Functions for manipulating the robot
    float setJointAngle(int jointID, float theta);
    float moveJointBy(int jointID, float delta);

    // Gets the RobotPose associated with the model
    virtual QSharedPointer<RobotPose> robotPose() const;


    //Starting a batch allows multiple changes to be made to the
    //robot without the events being fired for every little change.
    //This is used to move multiple joints, then refresh the view
    //only once so the motion is smooth.  It may have other uses
    //as well.  Batches can be nested (i.e., a batch can start another
    //batch).  When they are nested, only one signal will be emitted
    //at the end of the top-level batch
    bool batchMode();
    void startBatch();
    void endBatch();

    virtual void accept(RobotVisitor* visitor);

signals:
    //Emitted every time a joint changes its angle, regardless of
    //batch mode
    void jointAngleChanged(int jointID, float newTheta, float prevTheta);

    //Emitted when the robot changes.  If we are in batch mode,
    //however, it is only emitted after the call to endBatch(),
    //assuming the robot has changed during the batch
    void robotChanged();

};

#endif // ROBOT_H
